package cz.cvut.fel.wavrecordtest;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.EventListener;
import java.util.Calendar;
import java.math.*;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.CorrectionInfo;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import cz.cvut.fel.wavrecordtest.simpleMath;


public class WavRecordTest extends Activity {
	private MediaPlayer mPlayer = null;
	private AudioRecord mRecord = null;
	private playButton mPlay = null;
	private plotButton mPlot = null;	
	private statusTextView mText = null;
	private playAndRecordButton mPlayAndRec = null;
	private static final int RECORDER_SAMPLERATE = 44100;
	private static final int RECORDER_CHANNELS = AudioFormat.CHANNEL_IN_MONO;
	private static final int RECORDER_AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
	private Thread recordingThread = null;
	private boolean isRecording = false;
	int BufferElements2Rec = 1024; // want to play 2048 (2K) since 2 bytes we use only 1024
	int BytesPerElement = 2; // 2 bytes in 16bit format
	private static String filePath = null;	
	private Calendar cal;
	

	private void startPlaying() {
		mPlayer = new MediaPlayer();
		Uri path = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.test);
		try {
			mPlayer.setDataSource(getBaseContext(), path);
			mPlayer.prepare();
			mPlayer.start();
		} 
		catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}	
	}
	
	private void startRecording() {

	    mRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
	            RECORDER_SAMPLERATE, RECORDER_CHANNELS,
	            RECORDER_AUDIO_ENCODING, AudioRecord.getMinBufferSize(RECORDER_SAMPLERATE,
			            RECORDER_CHANNELS, RECORDER_AUDIO_ENCODING));

	    mRecord.startRecording();
	    isRecording = true;
	    recordingThread = new Thread(new Runnable() {
	        public void run() {
	            writeAudioDataToFile();
	        }
	    }, "AudioRecorder Thread");
	    recordingThread.start();
	    mText.setText("Recording now...");
	}		
	
	private void stopRecording() {
	    // stops the recording activity
	    if (null != mRecord) {
	        isRecording = false;
	        mRecord.stop();
	        mRecord.release();
	        mRecord = null;
	        //mText.setText("Recoding done.");
	    }
	}

	private double xplotData() {
		short[] plotData = {0, 0, 1, 0, 1, 0, 1, 2, 0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0};	
		double[] data_corr, data_corr_butter,data_sub;
		double distance, time;
		int maximum;
		int peak;

		//TODO When using real data limit the samples from 4300 to 22100 for processing
		/*
		data_corr = simpleMath.correlation(plotData);
		data_corr = simpleMath.abs(data_corr);
		data_corr_butter = simpleMath.butterworth(data_corr);
		peak = simpleMath.localMax(data_corr_butter);
		data_sub = simpleMath.getSubsequent(peak, peak+5, data_corr_butter);
		maximum = simpleMath.localMax(data_sub); */
		maximum = 3;
		
		time = (double)(maximum-64)/44100;
		distance = time*340;
		return distance;
	}
	
	private void plotData() {
		Long length = null;		
		byte[] buffer = null;
		short[] plotData = null;
		FileInputStream is = null;
		double[] data_corr = null;
		double[] data_corr_butter = null;
		double[] data_sub = null;
		double distance, time;
		int maximum;
		int peak;		
		if (filePath==null){ 
			mText.setText("No file recorded yet.");
			return;		
		}
		try {
		is = new FileInputStream(filePath);
		length = is.getChannel().size();
		buffer = new byte[length.intValue()];
		plotData = new short[length.intValue()];				
		is.read(buffer);		
		
		} catch(FileNotFoundException e) {
			e.printStackTrace();
		} catch(IOException e) {
			e.printStackTrace();
		}		
		plotData = byte2short(buffer);
		plotData = simpleMath.getSubsequent(4300, 22100, plotData);
		data_corr = new double[2*plotData.length];
		data_corr = simpleMath.correlation(plotData);
		data_corr = simpleMath.abs(data_corr);
		data_corr_butter = simpleMath.butterworth(data_corr);
		peak = simpleMath.localMax(data_corr_butter);
		data_sub = simpleMath.getSubsequent(peak+64, peak+400, data_corr_butter);
		maximum = simpleMath.localMax(data_sub);
		time = (double)(maximum+64)/44100;
		distance = time*340/2;
		mText.setText("The measured distance "+distance+" meters");
	}	
	
	private byte[] short2byte(short[] sData) {
	    int shortArrsize = sData.length;
	    byte[] bytes = new byte[shortArrsize * 2];
	    for (int i = 0; i < shortArrsize; i++) {
	        bytes[i * 2] = (byte) (sData[i] & 0x00FF);
	        bytes[(i * 2) + 1] = (byte) (sData[i] >> 8);
	        sData[i] = 0;
	    }
	    return bytes;

	}

	private short[] byte2short(byte[] bData) {
		short[] sData;
		short sDataTemp;
		int bDataLength = bData.length;
		int sDataLength;
		
		if (bDataLength%2>0) sDataLength=bDataLength/2+1;
		else sDataLength = bDataLength/2;
		
		sData = new short[sDataLength];
		
		for(int i=0;i<sDataLength;i++) {
			sData[i] = 0x0000;
			sDataTemp = 0x0000;
			sData[i] = bData[i*2+1];
			sData[i] = (short) (sData[i]<<8);
			sDataTemp = bData[i*2];
			sData[i] = (short) (sData[i]|sDataTemp);			
		}
		return sData;
	}
	
	private void writeAudioDataToFile() {
	    // Write the output audio in byte
		cal = Calendar.getInstance();
		int sec = cal.get(Calendar.SECOND);
		int min = cal.get(Calendar.MINUTE);
		int h = cal.get(Calendar.HOUR_OF_DAY);
		int d = cal.get(Calendar.DAY_OF_MONTH);
		int m = cal.get(Calendar.MONTH);
		int y = cal.get(Calendar.YEAR);
			
		filePath = Environment.getExternalStorageDirectory().getAbsolutePath();
        filePath += "/rec_"+d+"-"+m+"-"+y+"-"+h+min+sec+".wav";	    
	    short sData[] = new short[BufferElements2Rec];

	    FileOutputStream os = null;
	    try {
	        os = new FileOutputStream(filePath);
	    } catch (FileNotFoundException e) {
	        e.printStackTrace();
	    }
	    
	    while (isRecording) {
	        // gets the voice output from microphone to byte format

	        mRecord.read(sData, 0, BufferElements2Rec);
	        //System.out.println("Short writing to file" + sData.toString());
	        try {
	            // // writes the data to file from buffer
	            // // stores the voice buffer
	            byte bData[] = short2byte(sData);
	            os.write(bData, 0, BufferElements2Rec * BytesPerElement);
	            if (!mPlayer.isPlaying()) {
	            	stopRecording();
	            }
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	    }	    
	    try {
	        os.close();
	    } catch (IOException e) {
	        e.printStackTrace();
	    }	   
	}
	
	private void playAndRecord(){
		startPlaying();	
		startRecording();			
	}

	
	
	class playButton extends Button {
		
		OnClickListener click = new OnClickListener() {
			public void onClick(View v) {
				startPlaying();
			}
		};
		
		public playButton(Context ctx) {
			super(ctx);
			// TODO Auto-generated constructor stub			
			setText("Play test");
			setOnClickListener(click);
		}
	}
	
	
		class playAndRecordButton extends Button {
			
			OnClickListener click = new OnClickListener() {
				public void onClick(View v) {
					playAndRecord();					
				}
			};			
			
			public playAndRecordButton(Context context) {
				super(context);
				// TODO Auto-generated constructor stub
				setText("Play and Record");
				setOnClickListener(click);
			}
			
		}
		
		class plotButton extends Button {
			
			OnClickListener click = new OnClickListener() {
				public void onClick(View v) {
					plotData();
				}
			};
			
			public plotButton(Context ctx) {
				super(ctx);
				// TODO Auto-generated constructor stub			
				setText("Distance");
				setOnClickListener(click);
			}
		}	
		
		class statusTextView extends TextView {									
			public statusTextView(Context ctx) {
				super(ctx);
				setText("Program started, press any button required");				
			}
		}
		
	
		@Override
		protected void onCreate(Bundle savedInstanceState) {
			super.onCreate(savedInstanceState);
			LinearLayout ll = new LinearLayout(this);
			mPlay = new playButton(this);
			ll.addView(mPlay,
		            new LinearLayout.LayoutParams(
		                ViewGroup.LayoutParams.WRAP_CONTENT,
		                ViewGroup.LayoutParams.WRAP_CONTENT,
		                0));
			mPlayAndRec = new playAndRecordButton(this);
			ll.addView(mPlayAndRec,
		            new LinearLayout.LayoutParams(
		                ViewGroup.LayoutParams.WRAP_CONTENT,
		                ViewGroup.LayoutParams.WRAP_CONTENT,
		                0));
			mPlot = new plotButton(this);
			ll.addView(mPlot,
		            new LinearLayout.LayoutParams(
		                ViewGroup.LayoutParams.WRAP_CONTENT,
		                ViewGroup.LayoutParams.WRAP_CONTENT,
		                0));
			mText = new statusTextView(this);	
			ll.addView(mText,
					new LinearLayout.LayoutParams(
							ViewGroup.LayoutParams.WRAP_CONTENT,
							ViewGroup.LayoutParams.WRAP_CONTENT,
							0));
			//plot = new myXYPlot(this, "Simple graph");
			/*myPlot = new XYPlot(this, "Graph");
			ll.addView(myPlot,	
					new LinearLayout.LayoutParams(
							ViewGroup.LayoutParams.MATCH_PARENT,
							ViewGroup.LayoutParams.MATCH_PARENT,
							0));*/
			setContentView(ll);
		
			
		}	


	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.wav_record_test, menu);
		return true;
	}
	
	@Override
	protected void onDestroy() {
		// TODO Auto-generated method stub
		mPlayer.stop();
		mPlayer.release();		
		mRecord.stop();
		mRecord.release();
		super.onDestroy();
	}

}
